// 1、模拟 VueRouter 的 hash 模式的实现，实现思路和 History 模式类似，把 URL 中的 # 后面的内容作为路由的地址，可以通过 hashchange 事件监听路由地址的变化。

/**
 * 思路
 * Hash模式是基于锚点，通过锚点值作为路由地址；以及onhashchange事件，地址发生变化时触发onhashchange
 * Hash模式路由与history模式路由类似
 * 不同点在于router-link组件内的a标签href地址前添加'/#';href:'/#/ + this.to
 * a标签不需要添加事件阻止默认行为和记录历史，因为锚点并不会触发a标签的默认行为，而且会自动修改url
 * hash值发生变化的时候会触发onhashchange事件，监听该事件，把保存路由的响应式数据修改为对应地址，渲染对应的组件；window.addEventListener('hashchange',()=>{this.data.current = window.location.hash.substr(1)})
 */

let _Vue = null;

export default class VueRouter {
  static install(Vue) {
    //1.判断当前插件是否已经被安装
    if (VueRouter.install.installed) {
      return;
    }

    VueRouter.install.installed = true;
    // 2.把Vue构造函数记录到全局变量
    _Vue = Vue;
    // 3.把创建Vue实例时候传入的router对象注入到Vue实例
    // 使用混入，注意this指向，在外面this指向VueRouter，使用混入this指向Vue
    _Vue, mixin({
      beforeCreate() {
        // vue实例执行；组件不执行，组件的$options不存在router属性
        if (this.$options.router) {
          // 把入口文件中new Vue()传入的router挂载到$router
          _Vue.prototype.$router = this.$options.router;
          // 入口实例化Vue的时候传入实例化的VueRouter，此时router挂载到vue的$options属性上
          this.$options.router.init();
        }
      }
    });
  }

  constructor(options) {
    // 记录构造函数传入的选项
    this.options = options;
    // 把路由规则解析存储到routerMap，键：路由地址，值：路由组件，将来router-view组件会根据当前路由地址到routerMap对象找到对应的组件渲染到浏览器
    this.routeMap = {};
    // data是一个响应式对象，存储当前的路由地址，路由变化是自动加载组件
    this.data = _Vue.observable({
      // vue提供的创建响应式对象的方法
      // 存储当前的路由地址，默认是'/'
      current: window.location.hash.substr(1) || '/'
    });
  }

  init() {
    this.createRouterMap();
    this.initComponents(_Vue);
    this.initEvent();
  }

  createRouterMap() {
    // 遍历所有路由规则，把路由规则解析为键值对形式，存储到routerMap对象
    this.options.routes.forEach(route => {
      this.routeMap[route.path] = route.component;
    });
  }

  initComponents(Vue) {
    Vue.component('router-link', {
      props: {
        to: String
      },
      render(h) {
        // h函数为我们创建虚拟DOM
        // 参数一：选择器；参数二：DOM元素设置属性；参数三：标签之间的内容，内容可能有很多所以是数组
        return h('a', {
          attrs: {
            href: '/#' + this.to
          }
        }, [this.$slots.default]);
      }
    });

    const self = this;
    Vue.component('router-view', {
      render(h) {
        // render函数里的this不是指向Vue实例
        // 获取当前路由地址对应的路由组件，嗲用h函数把组件转换为虚拟DOM返回
        // 获取当前路由对应的组件，data是响应式的，当前地址改变后将更新对应的页面
        const component = self.routeMap[self.data.current];
        return h(component);
      }
    });
  }

  initEvent() {
    // 当浏览器前进后退时
    window.addEventListener('hashchange', () => {
      this.data.current = window.location.hash.substr(1);
    });
  }
}